/*
  AdViEmulator - AdventureVision emulator
  Copyright (C) 2012-2013  JustBurn

  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation, either version 3 of the License, or
  (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#ifndef CORE8048_H
#define CORE8048_H

#include <stdint.h>

// Note: Interrupt support is disabled
//       INT pin always read 1
//       T0 pin always read 1

class Core8048
{
public:
	enum PSW_FLAGS {	// PSW
		STACK = (1 << 0),
		SET = (1 << 3),
		RB = (1 << 4),
		FLAG0 = (1 << 5),
		AUXCARRY = (1 << 6),
		CARRY = (1 << 7)
	};

	enum TMRSEL {		// TMRSEL
		TMRSEL_OFF,		// Disabled
		TMRSEL_OUT,		// External, T1 pin
		TMRSEL_IN		// Internal, 1/32 clock
	};

	enum Exception {	// Exception
		INVALID_INSTRUCTION
	};

	Core8048();
	void Reset(void);
	int ExecSingle(void);

	// Tables
	static const char *InstructionName[256];
	static const uint8_t InstructionSize[256];
	static const uint8_t InstructionCycle[256];
	void GetInstructionName(const uint8_t *dopc, uint16_t addr, char *sout);
	void GetInstructionName(uint16_t addr, char *sout);
	int GetInstructionSize(uint16_t addr);
	int GetInstructionCycles(uint16_t addr);

	// Callbacks
	virtual uint8_t OnDecodeROM(uint16_t addr) = 0;
	virtual uint8_t OnReadROM(uint16_t addr) = 0;
	virtual uint8_t OnReadRAM(uint8_t addr) = 0;
	virtual void OnWriteRAM(uint8_t addr, uint8_t data) = 0;
	virtual uint8_t OnReadEx(uint8_t addr) = 0;
	virtual void OnWriteEx(uint8_t addr, uint8_t data) = 0;
	virtual uint8_t OnPortRd(uint8_t port) = 0;
	virtual void OnPortWr(uint8_t port, uint8_t data) = 0;
	virtual void OnException(Exception exception, int value) = 0;

	// Registers
	uint8_t accu;		// Accumulator
	uint8_t tcnt;		// Timer counter
	uint8_t tcnt_ps;	// Timer prescaler
	union Tpsw {
		struct {
			uint8_t stack:3;	// Stack depth
			uint8_t one:1;		// Always 1
			uint8_t rb:1;		// Register Bank
			uint8_t f0:1;		// Flag 0
			uint8_t ac:1;		// Alternate Carry
			uint8_t c:1;		// Carry
			// Not accessible:
			uint8_t f1:1;		// Flag 1
			uint8_t mb:1;		// Memory Bank
			uint8_t unused:6;	// Unused
		};
		struct {
			uint8_t b;
			uint8_t bx;
		};
		uint16_t w;
	} psw;
	union Tff {
		struct {
			uint8_t intf:1;		// Interrupt flag
			uint8_t tf:1;		// Timer overflow flag
			uint8_t tcnti:1;	// Timer counter interrupt request
		};
		uint16_t w;
	} ff;
	uint8_t tmrsel;			// Timer select (0 = Stop, 1 = T1 Pin, 2 = 1/32 Clock)
	uint8_t gio_t1;			// Test 1 signal
	uint8_t gio_t1Last;		// Test 1 signal (previous signal to test for transitions)
	union Tpc {
		struct {
			uint8_t low;
			uint8_t high;
		};
		uint16_t w;
	} pc;
	uint16_t framecnt;

private:
	int ExecSingle_Internal(void);
	uint8_t ReadPC(void);
	uint8_t ReadReg(int reg);
	void WriteReg(int reg, uint8_t value);
	uint8_t ReadRegMem(int reg);
	void WriteRegMem(int reg, uint8_t value);
	void Push(void);
	void Pop(bool restorepsw);
	void Jump(int addr);
	void Jump8(uint8_t addr);
	void Call(int addr);
	void ADD(uint8_t value);
	void ADDC(uint8_t value);
};

#endif // CORE8048_H
